From 4a0a4459e934bc73e3bce714bfd01c16e90c7ce2 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Mon, 4 Dec 2006 17:52:33 +0000 Subject: [PATCH] Fix numpad handling in QEMU's VNC server. The keymaps that we have include information on which keys change depending upon the numlock setting, but this isn't being used. By forcing numlock on and off as necessary, when receiving these keysyms through the VNC connection, we ensure that the server's numlock status is the same as the client's. Signed-off-by: Ewan Mellor --- tools/ioemu/keymaps.c | 63 ++++++++++++++++++++++++++++++++++++++-- tools/ioemu/vnc.c | 32 ++++++++++++++++++++ tools/ioemu/vnc_keysym.h | 14 +++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/tools/ioemu/keymaps.c b/tools/ioemu/keymaps.c index bd893288aa..e7dd4a8b11 100644 --- a/tools/ioemu/keymaps.c +++ b/tools/ioemu/keymaps.c @@ -36,8 +36,10 @@ static int get_keysym(const char *name) #define MAX_EXTRA_COUNT 256 typedef struct { uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + int keysym2numlock[MAX_NORMAL_KEYCODE]; struct { int keysym; + int numlock; uint16_t keycode; } keysym2keycode_extra[MAX_EXTRA_COUNT]; int extra_count; @@ -50,6 +52,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, char file_name[1024]; char line[1024]; int len; + int *keycode2numlock; + int i; snprintf(file_name, sizeof(file_name), "%s/keymaps/%s", bios_dir, language); @@ -63,6 +67,15 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, "Could not read keymap file: '%s'\n", file_name); return 0; } + + /* Allocate a temporary map tracking which keycodes change when numlock is + set. Keycodes are 16 bit, so 65536 is safe. */ + keycode2numlock = malloc(65536 * sizeof(int)); + if (!keycode2numlock) { + perror("Could not read keymap file"); + return 0; + } + for(;;) { if (fgets(line, 1024, f) == NULL) break; @@ -86,13 +99,19 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, if (keysym == 0) { // fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { - const char *rest = end_of_keysym + 1; - int keycode = strtol(rest, NULL, 0); + char *rest = end_of_keysym + 1; + int keycode = strtol(rest, &rest, 0); + int numlock = (rest != NULL && + strstr(rest, "numlock") != NULL); + + keycode2numlock[keycode] = numlock; + /* if(keycode&0x80) keycode=(keycode<<8)^0x80e0; */ if (keysym < MAX_NORMAL_KEYCODE) { //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); k->keysym2keycode[keysym] = keycode; + k->keysym2numlock[keysym] = numlock; } else { if (k->extra_count >= MAX_EXTRA_COUNT) { fprintf(stderr, @@ -107,6 +126,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, keysym = keysym; k->keysym2keycode_extra[k->extra_count]. keycode = keycode; + k->keysym2keycode_extra[k->extra_count]. + numlock = numlock; k->extra_count++; } } @@ -115,6 +136,22 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, } } fclose(f); + + for (int i = 0; i < MAX_NORMAL_KEYCODE; i++) { + if (k->keysym2numlock[i] != 1) { + k->keysym2numlock[i] = -keycode2numlock[k->keysym2keycode[i]]; + } + } + + for (int i = 0; i < k->extra_count; i++) { + if (k->keysym2keycode_extra[i].numlock != 1) { + k->keysym2keycode_extra[i].numlock = + -keycode2numlock[k->keysym2keycode_extra[i].keycode]; + } + } + + free(keycode2numlock); + return k; } @@ -143,3 +180,25 @@ static int keysym2scancode(void *kbd_layout, int keysym) } return 0; } + +/** + * Returns 1 if the given keysym requires numlock to be pressed, -1 if it + * requires it to be cleared, and 0 otherwise. + */ +static int keysym2numlock(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + if (keysym < MAX_NORMAL_KEYCODE) { + return k->keysym2numlock[keysym]; + } else { + int i; +#ifdef XK_ISO_Left_Tab + if (keysym == XK_ISO_Left_Tab) + keysym = XK_Tab; +#endif + for (i = 0; i < k->extra_count; i++) + if (k->keysym2keycode_extra[i].keysym == keysym) + return k->keysym2keycode_extra[i].numlock; + } + return 0; +} diff --git a/tools/ioemu/vnc.c b/tools/ioemu/vnc.c index 86d3720f90..6d7cc31d4a 100644 --- a/tools/ioemu/vnc.c +++ b/tools/ioemu/vnc.c @@ -115,6 +115,7 @@ struct VncState int ctl_keys; /* Ctrl+Alt starts calibration */ int shift_keys; /* Shift / CapsLock keys */ + int numlock; }; #define DIRTY_PIXEL_BITS 64 @@ -854,14 +855,40 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) } } +static void press_key(VncState *vs, int keycode) +{ + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) & 0x7f); + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) | 0x80); +} + static void do_key_event(VncState *vs, int down, uint32_t sym) { sym &= 0xFFFF; if (is_graphic_console()) { int keycode; + int numlock; keycode = keysym2scancode(vs->kbd_layout, sym); + numlock = keysym2numlock(vs->kbd_layout, sym); + + /* If the numlock state needs to change then simulate an additional + keypress before sending this one. This will happen if the user + toggles numlock away from the VNC window. + */ + if (numlock == 1) { + if (!vs->numlock) { + vs->numlock = 1; + press_key(vs, XK_Num_Lock); + } + } + else if (numlock == -1) { + if (vs->numlock) { + vs->numlock = 0; + press_key(vs, XK_Num_Lock); + } + } + if (keycode & 0x80) kbd_put_keycode(0xe0); if (down) @@ -932,6 +959,10 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) vs->shift_keys ^= 2; break; + case XK_Num_Lock: + vs->numlock = !vs->numlock; + break; + case XK_1 ... XK_9: if ((vs->ctl_keys & 3) != 3) break; @@ -1355,6 +1386,7 @@ int vnc_display_init(DisplayState *ds, int display, int find_unused, struct sock vs->lsock = -1; vs->csock = -1; vs->depth = 4; + vs->numlock = 0; vs->ds = ds; diff --git a/tools/ioemu/vnc_keysym.h b/tools/ioemu/vnc_keysym.h index 14fe47f9e1..5c47104442 100644 --- a/tools/ioemu/vnc_keysym.h +++ b/tools/ioemu/vnc_keysym.h @@ -231,6 +231,19 @@ static name2keysym_t name2keysym[]={ {"Home", 0xff50}, /* XK_Home */ {"End", 0xff57}, /* XK_End */ {"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ +{"KP_Home", 0xff95}, +{"KP_Left", 0xff96}, +{"KP_Up", 0xff97}, +{"KP_Right", 0xff98}, +{"KP_Down", 0xff99}, +{"KP_Prior", 0xff9a}, +{"KP_Page_Up", 0xff9a}, +{"KP_Next", 0xff9b}, +{"KP_Page_Down", 0xff9b}, +{"KP_End", 0xff9c}, +{"KP_Begin", 0xff9d}, +{"KP_Insert", 0xff9e}, +{"KP_Delete", 0xff9f}, {"F1", 0xffbe}, /* XK_F1 */ {"F2", 0xffbf}, /* XK_F2 */ {"F3", 0xffc0}, /* XK_F3 */ @@ -258,6 +271,7 @@ static name2keysym_t name2keysym[]={ {"KP_8", 0xffb8}, /* XK_KP_8 */ {"KP_9", 0xffb9}, /* XK_KP_9 */ {"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Separator", 0xffac},/* XK_KP_Separator */ {"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ {"KP_Divide", 0xffaf}, /* XK_KP_Divide */ {"KP_Enter", 0xff8d}, /* XK_KP_Enter */ -- 2.30.2